iT邦幫忙

2023 iThome 鐵人賽

DAY 3
0
Web 3

以 Python 進入以太坊虛擬機 (EVM) 的幻想境界系列 第 3

虛擬境界 3:Stack (PUSH & POP)

  • 分享至 

  • xImage
  •  

在以太坊虛擬機(Ethereum Virtual Machine,簡稱 EVM)中,堆疊(stack)是一個重要的資料結構,遵循後入先出(LIFO)的原則。程式可以透過 PUSH 將資料放入堆疊,並使用 POP 從堆疊中取出資料。

首先,我們會將程式碼中的 opcode 挑出,以便進一步執行相應的操作。

class EVM:
    def __init__(self, code) -> None:
        self.code = code
        self.pc = 0
        self.stack = []
    
		# Program Counter Return Execute Opcodes
    def next(self):
        opCode = self.code[self.pc]
        self.pc += 1
        return opCode

		# Run EVM until code end
    def execute(self):
        while self.pc < len(self.code):
            opCode = self.next()

						# Stack Operation
            if opcodes.PUSH0 <= opCode and opcodes.PUSH32 or opCode == opcodes.POP:
                stack.Stack(self, opCode)
              
# main
if __name__ == '__main__':
    code =  b"\x01\x01"
    evm = EVM(code)
    evm.execute()

這段 code 主要會分類會使用的 opcode,將該指令傳入 class 處理當前 stack

if opcodes.PUSH0 <= opCode and opcodes.PUSH32 or opCode == opcodes.POP:
		stack.Stack(self, opCode)

而在這部分我們會實現 stack 的 class,由 EVM 將 code 傳進來處理,而 Stack 會根據 opcode 來計算要傳入多少大小的字節進 evm 的 stack

PUSH 指令概述

PUSH 指令允許我們將數據推送到堆疊中。以太坊升級後,引入了多個 PUSH 指令,可以將不同大小的數據推送到堆疊中。

以太坊上海升級後新增了 PUSH0 ,該 opcode 負責將 0 加入 stack,而 PUSH1 (0x60) - PUSH32(0x7F),都可以向以下去實現,data 就是 PUSH 的 size 而這個 size 會由 OPCODE - PUSH1 的 opcode 來推斷要加入多少byte 進 stack,我們透過計算出 PUSH 指令應推送的字節大小,然後進行推送操作。

size = opCode - PUSH1 + 1
push(size)  
def push(self, size):
		# 從程式碼中取得要推送的數據
    data = self.evm.code[self.evm.pc : self.evm.pc + size]
    value = int.from_bytes(data, byteorder='big')
    self.evm.stack.append(value)
    self.evm.pc += size

POP 指令概述

POP 指令用於從堆疊中移除數據。

def pop(self):
    return self.evm.stack.pop()

這個 pop 函數可以讓我們從堆疊中移除頂部的數據。

Stack 指令包含

#
# POP Operations
#

POP = 0x50
#
# Push Operations
#
PUSH0 = 0x5F
PUSH1 = 0x60
PUSH2 = 0x61
PUSH3 = 0x62
PUSH4 = 0x63
PUSH5 = 0x64
PUSH6 = 0x65
PUSH7 = 0x66
PUSH8 = 0x67
PUSH9 = 0x68
PUSH10 = 0x69
PUSH11 = 0x6A
PUSH12 = 0x6B
PUSH13 = 0x6C
PUSH14 = 0x6D
PUSH15 = 0x6E
PUSH16 = 0x6F
PUSH17 = 0x70
PUSH18 = 0x71
PUSH19 = 0x72
PUSH20 = 0x73
PUSH21 = 0x74
PUSH22 = 0x75
PUSH23 = 0x76
PUSH24 = 0x77
PUSH25 = 0x78
PUSH26 = 0x79
PUSH27 = 0x7A
PUSH28 = 0x7B
PUSH29 = 0x7C
PUSH30 = 0x7D
PUSH31 = 0x7E
PUSH32 = 0x7F
class Stack:
    def __init__(self, evm, opCode) -> []:
        self.evm = evm
        if opCode == PUSH0:
            evm.stack.append(0)
        elif PUSH1 <= opCode and opCode <= PUSH32:
            size = opCode - PUSH1 + 1
            self.push(size)
        elif opCode == POP:
            self.pop()
    
    def push(self, size):
        data = self.evm.code[self.evm.pc : self.evm.pc + size]
        value = int.from_bytes(data, byteorder='big')
        self.evm.stack.append(value)
        self.evm.pc += size

    def pop(self):
        return self.evm.stack.pop()

現在,你已經有了基本的 PUSH 和 POP 指令的實現,可以繼續擴充你的 EVM 功能,如 DUP 和 SWAP 指令。如果需要更多幫助,隨時提問!


上一篇
虛擬境界 2:OPCODES 運作
下一篇
虛擬境界 4:Stack (DUP & SWAP)
系列文
以 Python 進入以太坊虛擬機 (EVM) 的幻想境界30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言